let Promise = require('bluebird');
let fs = require('fs');
let _ = require('lodash');
let request = Promise.promisify(require('request'));
let prefix = 'https://api.weixin.qq.com/cgi-bin/';
let api = {
  tokenPre: prefix + 'token?grant_type=client_credential',
  temporary: {
    upload: prefix + 'media/upload?',
    fetch: prefix + 'media/get?',
  },
  permanent: {
    upload: prefix + 'material/add_material?',
    fetch: prefix + 'material/get_material?',
    del: prefix + 'material/del_material?',
    update: prefix + 'material/update_news?',
    count: prefix + 'material/get_materialcount?',
    batch: prefix + 'material/batchget_material?',
    uploadNews: prefix + 'material/add_news?',
    uploadNewsPic: prefix + 'media/uploadimg?'
  },
  group: {
    create: prefix + 'tags/create?',
    get: prefix + 'tags/get?',
    update: prefix + 'tags/update?',
    delete: prefix + 'tags/delete?',
    getUsers: prefix + 'user/tag/get?',
    batchtagging: prefix + 'tags/members/batchtagging?',
    unBatchtagging: prefix + 'tags/members/batchuntagging?',
    getidlist: prefix + 'tags/getidlist?',
  },
  user: {
    fetch: prefix + 'user/info?',
    get: prefix + 'user/get?',
  },
  mass: {
    sendAll: prefix + 'message/mass/sendall?'
  },
  menu: {
    create: prefix + 'menu/create?',
    delete: prefix + 'menu/delete?'
  },
  semantic: 'https://api.weixin.qq.com/semantic/semproxy/search?',
  voice: {
    add: prefix + 'media/voice/addvoicetorecofortext?'
  },
  ticket: {
    fetch: prefix + 'ticket/getticket?'
  }
};

let Wechat = function (opts) {
  let that = this;
  this.appID = opts.appID;
  this.appSecret = opts.appSecret;
  this.getAccessToken = opts.getAccessToken;
  this.saveAccessToken = opts.saveAccessToken;
  this.getTicket = opts.getTicket;
  this.saveTicket = opts.saveTicket;

  this.getAccessToken()
    .then(data => {
      try {
        data = JSON.parse(data)
      } catch(e) {
        return that.updateAccessToken()
      }
      if(that.isValidAccessToken(data)) {
        return Promise.resolve(data)
      } else {
        return that.updateAccessToken()
      }
    })
    .then(data => {
      that.access_token = data.access_token;
      that.expires_in = data.expires_in;
      that.saveAccessToken(data)
    })
};
//菜单相关
  //1. 创建菜单
Wechat.prototype.createMenu = function (menu) {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.menu.create + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: menu, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
  //2. 删除菜单
Wechat.prototype.deleteMenu = function (menu) {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.menu.delete + 'access_token=' + access_token;
      request({method: 'GET', url: url, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

// 智能语义接口 addvoice还未完善
Wechat.prototype.semanticData = function (semanticData) {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.semantic + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: semanticData, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
Wechat.prototype.addVoice = function (voiceId) {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.voice.add + 'access_token=' + access_token + '&format=&voice_id=' + voiceId + '&lang=zh_CN';
      url =  url.replace('https://', 'http://')
      console.log(url)
      request({method: 'POST', url: url, json: true}).then(res => {
        let result = res.body;
        console.log(result)
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

//用户信息相关
  //1. 获取单个用户信息， 多个的有需求再添加
Wechat.prototype.getUserInfo = function (openid) {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.user.fetch + 'access_token=' + access_token + '&openid=' + openid + '&lang=zh_CN';
      request({method: 'GET', url: url, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
  //2. 获取所有用户列表
Wechat.prototype.getUsersList = function (next_openid) {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.user.get + 'access_token=' + access_token;
      if (next_openid) url = api.user.get + 'access_token=' + access_token + '&next_openid' + next_openid;
      request({method: 'GET', url: url, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

Wechat.prototype.isValidAccessToken = (data) => {
  if(!data || !data.access_token || !data.expires_in) {
    return false
  }
  let expires_in = data.expires_in;
  let now = (new Date().getTime());
  if (now < expires_in) {
    return true
  } else {
    return false
  }
};
Wechat.prototype.updateAccessToken = function() {
  let appID = this.appID;
  let appSecret = this.appSecret;
  let url = api.tokenPre + '&appid=' + appID + '&secret=' + appSecret;
  return new Promise((resolve,reject)=> {
    request({url: url,json:true}).then((res) => {
      let data = res.body;
      let now = (new Date().getTime());

      data.expires_in = now + (data.expires_in - 20) * 1000;
      resolve(data)
    })
  })
};
Wechat.prototype.fetchAccessToken = function() {
  let that = this;
  that.getAccessToken()
    .then(data => {
      try {
        data = JSON.parse(data)
      } catch(e) {
        return that.updateAccessToken()
      }
      if(that.isValidAccessToken(data)) {
        return Promise.resolve(data)
      } else {
        return that.updateAccessToken()
      }
    })
    .then(res => {
      that.access_token = res.access_token;
      that.expires_in = res.expires_in;
      that.saveAccessToken(res)
    });
  return new Promise(resolve =>{
    resolve(that.access_token)
  })
};
// 上传素材相关
Wechat.prototype.upLoadMaterial = function (type, material, permanent) {
  let that = this;
  let form = {};
  let uploadUrl = api.temporary.upload;

  if (permanent) {
    uploadUrl = api.permanent.upload;
    _.extend(form, permanent)
  }
  if (type === 'pic') {
    uploadUrl = api.permanent.uploadNewsPic;

  }
  if (type === 'news') {
    uploadUrl = api.permanent.uploadNews;
    form = material
  } else {
    form.media = fs.createReadStream(material)
  }

  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = uploadUrl + 'access_token=' + access_token;
      if (!permanent) {
        url = uploadUrl + 'access_token=' + access_token + '&type=' + type
      } else {
        form.access_token = access_token
      }
      let options = {
        method: 'POST',
        json: true ,
        url: url
      };
      if (type === 'news') {
        options.body = form
      } else {
        options.formData = form
      }
      request(options).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('upload Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

Wechat.prototype.fetchMaterial = function (mediaId, type, permanent) {
  let that = this;
  let fetchUrl = api.temporary.fetch;

  if (permanent) {
    fetchUrl = api.permanent.fetch;
  }

  return new Promise(function (resolve, reject) {
    that.fetchAccessToken().then(access_token => {
      let url = fetchUrl + 'access_token=' + access_token;
      let options = {method: 'POST', url: url, json: true}
      let form = {};
      if (permanent) {
        form.media_id = mediaId;
        form.access_token = access_token;
        options.body = form
      } else {
        if (type === 'video') {
          url = url.replace('https://', 'http://')
        }
        url += '&media_id=' + mediaId
      }
      if (type === 'news' || type === 'video') {
        request(options).then(res => {
          let result = res.body;
          if(result) {
            resolve(result)
          } else {
            return new Error('fetch Wrong')
          }
        }).catch(err => {
          console.log(err)
        })

      } else {
        resolve(url)
      }

    })
  })
};

Wechat.prototype.deleteMaterial = function (mediaId) {
  let that = this;
  let form = {
    media_id: mediaId
  };
  return new Promise(function (resolve, reject) {
    that.fetchAccessToken().then(access_token => {
      let url = api.permanent.del + 'access_token=' + access_token + '&media_id=' + mediaId;
      request({method: 'POST', url: url, body: form, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('delete Wrong')
        }
      }).catch(err => {
        console.log(err)
      })

    })
  })
};

Wechat.prototype.updateMaterial = function (mediaId, news) {
  let that = this;
  let form = {
    media_id: mediaId
  };
  _.extend(form, news)
  return new Promise(function (resolve, reject) {
    that.fetchAccessToken().then(access_token => {
      let url = api.permanent.update + 'access_token=' + access_token + '&media_id=' + mediaId;
      request({method: 'POST', url: url, body: form, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('delete Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

Wechat.prototype.countMaterial = function () {
  let that = this;
  return new Promise(function (resolve, reject) {
    that.fetchAccessToken().then(access_token => {
      let url = api.permanent.count + 'access_token=' + access_token;
      request({method: 'GET', url: url, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('count Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

Wechat.prototype.batchMaterial = function (opts) {
  let that = this;
  let options = {};
  options.type = opts ? opts.type : 'image';
  options.offset = opts ? opts.offset : 0;
  options.count = opts ? opts.count : 1;
  return new Promise(function (resolve, reject) {
    that.fetchAccessToken().then(access_token => {
      let url = api.permanent.batch + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: options, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('count Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

// 用户分组相关
// 1.创建分组
Wechat.prototype.createGroup = function (name) {
  let that = this;
  let form = {
    tag: {
      name: name
    }
  };
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.create + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
// 2.获取现在的分组
Wechat.prototype.getGroup = function () {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.get + 'access_token=' + access_token;
      request({method: 'GET', url: url, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
// 3.重命名分组
Wechat.prototype.updateGroup = function (id, name) {
  let that = this;
  let form = {
    tag: {
      id: id,
      name: name
    }
  };
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.update + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
//4.删除分组
Wechat.prototype.updateGroup = function (id) {
  let that = this;
  let form = {
    tag: {
      id: id,
    }
  };
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.delete + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
//5. 获取标签下粉丝列表
Wechat.prototype.getUsersGroup = function (id) {
  let that = this;
  let form = {
    tagid: id
  }
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.getUsers + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form,json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
//6. 批量为用户打标签
Wechat.prototype.batchtaggingGroup = function (openidList, tagid) {
  let that = this;
  let form = {
    openid_list: openidList,
    tagid: tagid
  }
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.batchtagging + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form,json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
//6. 批量为用户取消标签
Wechat.prototype.batchuntaggingGroup = function (openidList, tagid) {
  let that = this;
  let form = {
    openid_list: openidList,
    tagid: tagid
  }
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.unBatchtagging + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form,json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};
//6. 获取用户身上的标签列表
Wechat.prototype.batchuntaggingGroup = function (openid) {
  let that = this;
  let form = {
    openid: openid,
  }
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.group.getidlist + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form,json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('create group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

//群发消息相关
//发给所有，这里目前只实现了群发给所有文本， 有需求发送图文等，后续补充
Wechat.prototype.sendAll = function (type, message, groupIds) {
  let that = this;
  let form = {
    filter:{
      is_to_all: true,
    },
    msgtype: type
  };
  form[type] = message;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.mass.sendAll + 'access_token=' + access_token;
      request({method: 'POST', url: url, body: form, json: true}).then(res => {
        let result = res.body;
        if(result) {
          resolve(result)
        } else {
          return new Error('sendAll group Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

//获取ticket
Wechat.prototype.fetchTicket = async function() {
  let that = this;
  let ticket = await that.getTicket()
    .then(data => {
      try {
        data = JSON.parse(data)
      } catch(e) {
        return that.updateTicket()
      }
      if(that.isValidTicket(data)) {
        return Promise.resolve(data)
      } else {
        return that.updateTicket()
      }
    })
    .then(res => {
      that.ticket = res.ticket;
      that.expires_in = res.expires_in;
      that.saveTicket({
        ticket: res.ticket,
        expires_in: res.expires_in
      })
      return Promise.resolve(that.ticket)
    });
  return ticket
};
Wechat.prototype.isValidTicket = (data) => {
  if(!data || !data.ticket || !data.expires_in) {
    return false
  }
  let expires_in = data.expires_in;
  let now = (new Date().getTime());
  if (now < expires_in) {
    return true
  } else {
    return false
  }
};

Wechat.prototype.updateTicket = function (access_token) {
  let that = this;
  return new Promise(function (resolve) {
    that.fetchAccessToken().then(access_token => {
      let url = api.ticket.fetch + 'access_token=' + access_token + '&type=jsapi';
      request({method: 'GET', url: url, json: true}).then(res => {
        let data = res.body;
        let now = (new Date().getTime());
        data.expires_in = now + (data.expires_in - 20) * 1000;
        if(data) {
          resolve(data)
        } else {
          return new Error('update Ticket Wrong')
        }
      }).catch(err => {
        console.log(err)
      })
    })
  })
};

module.exports = Wechat;
